package com.leyou.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.utils.CookieUtils;
import com.leyou.config.RsaKeyProperties;
import com.leyou.domain.SysRole;
import com.leyou.domain.SysUser;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//继承UsernamePasswordAuthenticationFilter类，从写认证方法
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private RsaKeyProperties prop;  //生成token用

    //谁用这个过滤器对象，就传递上面两个对象的值进来
    public JwtLoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties prop) {
        this.authenticationManager = authenticationManager;
        this.prop = prop;
    }

    //接收并解析用户凭证，出現错误时，返回json数据前端
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            //jackson提供的方法，读取数据流并 将json格式请求体转成JavaBean对象。
            SysUser sysUser = new ObjectMapper().readValue(request.getInputStream(), SysUser.class);
            //两个参数都是用户输入过来的，不是数据库中的。
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword());
            return authenticationManager.authenticate(authRequest); //自动去比对数据
        } catch (Exception e) {
            try {
                //如果认证失败，提供自定义json格式异常
                response.setContentType("application/json;charset=utf-8");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                PrintWriter out = response.getWriter();
                Map resultMap = new HashMap();
                resultMap.put("code", HttpServletResponse.SC_UNAUTHORIZED);
                resultMap.put("msg", "用户名或密码错误！");
                out.write(new ObjectMapper().writeValueAsString(resultMap));
                out.flush(); //强制输出
                out.close(); //关闭流
            } catch (IOException outEx) {
                outEx.printStackTrace();
            }
            throw new RuntimeException(e); //异步请求该异常用户是看不见的，所以上面自定义异常时前端可接收的异步数据。
        }
    }

    //用户登录成功后，成token生,并且返回json数据给前端。参数3：authResult可以得到用户所有的属性。
    public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //得到当前认证的用户对象
        SysUser user = new SysUser();
        //token中不放密码。
        user.setUsername(authResult.getName());
        user.setId(((SysUser) authResult.getPrincipal()).getId());
        user.setRoles((List<SysRole>) authResult.getAuthorities());
        //生成token，过期时间以分钟计算。 参数1：用户对象。 参数2：私钥。 参数3：过期时间。
        String token = JwtUtils.generateTokenExpireInMinutes(user, prop.getPrivateKey(), 24 * 60);
        //存到cookie中返回
        CookieUtils.newCookieBuilder().
                response(response).
                domain(prop.getUser().getCookieDomain()).
                name(prop.getUser().getCookieName()).
                value(token).
                httpOnly(true).
                build();
        try {
            //登录成功時，返回json格式进行提示
            response.setContentType("application/json;charset=utf-8");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter out = response.getWriter();
            Map resultMap = new HashMap();
            resultMap.put("code", HttpServletResponse.SC_OK);
            resultMap.put("msg", "认证通过！");
            out.write(new ObjectMapper().writeValueAsString(resultMap));
            out.flush(); //强制输出
            out.close(); //关闭流
        } catch (IOException outEx) {
            outEx.printStackTrace();
        }
    }
}

